package manager

import (
	"context"
	"fmt"
	"gitee.com/xfrm/structs"
	"reflect"
	"strconv"
	"strings"
)

//根据对象的属性(按照structs包获取属性的规则)，以及属性的值，构建sql的map数据。
//属性的值是字符串，根据属性的类型，将字符串转成对应的类型
//场景：在修改用户信息的接口中，传入是要修改的属性以及值（前端传入，全部为字符串），在实际执行修改之前，通过该函数构造sql语句
func BuildUpdateFiledMap(ctx context.Context, o interface{}, attrKey, attrValue string) (*structs.Field, map[string]interface{}, error) {
	oStruct := structs.New(o)
	oAttr, ok := oStruct.FieldOk(attrKey)
	if !ok {
		return nil, nil, fmt.Errorf("invalid attrkey")
	}
	var ups = make(map[string]interface{})
	var val interface{}
	//只处理 数字、字符串、bool型的属性更新
	switch oAttr.Kind() {
	case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int8:
		v, err := strconv.ParseInt(attrValue, 10, 64)
		if err != nil {
			return nil, nil, err
		}
		val = v
	case reflect.String:
		val = attrValue
	case reflect.Bool:
		attrValue = strings.ToLower(attrValue)
		val = attrValue == "true"
	default:
		return nil, nil, fmt.Errorf("not support attr kind: %s", oAttr.Kind())
	}
	//注意：此处和entity中定义的对象使用的标签绑定
	dbColumnName, _ := oAttr.Tag("bdb")
	if dbColumnName == "" {
		return nil, nil, fmt.Errorf("invalid attr not found column: %s", attrKey)
	}
	ups[dbColumnName] = val
	return oAttr, ups, nil
}

//将对象所有属性，构造成sql的map对象
//可以用在：插入、查询、查询条件等sql的map构造场景
func BuildDbSqlMap(ctx context.Context, o interface{}, omitZero bool) (map[string]interface{}, error) {
	oStruct := structs.New(o)
	fields := oStruct.Fields()
	if len(fields) == 0 {
		return nil, fmt.Errorf("no fields")
	}
	var ups = make(map[string]interface{})
	for _, field := range fields {
		//注意：此处和entity中定义的对象使用的标签绑定
		dbColumnName, _ := field.Tag("bdb")
		if dbColumnName == "" {
			continue
		}
		if omitZero && field.IsZero() {
			continue
		}
		ups[dbColumnName] = field.Value()
	}
	if len(ups) == 0 {
		return nil, fmt.Errorf("no valid fields error")
	}
	return ups, nil
}

type BuildOption struct {
	OmitZero       bool
	ExcludeColumns []string
	IncludeColumns []string
}

func BuildSqlMap(ctx context.Context, o interface{}, option BuildOption) (map[string]interface{}, error) {
	oStruct := structs.New(o)
	fields := oStruct.Fields()
	if len(fields) == 0 {
		return nil, fmt.Errorf("no fields")
	}
	var includeMap = make(map[string]interface{})
	for _, incl := range option.IncludeColumns {
		includeMap[incl] = struct {
		}{}
	}
	var excludeMap = make(map[string]interface{})
	for _, incl := range option.ExcludeColumns {
		excludeMap[incl] = struct {
		}{}
	}
	var ups = make(map[string]interface{})
	for _, field := range fields {
		//注意：此处和entity中定义的对象使用的标签绑定
		dbColumnName, _ := field.Tag("bdb")
		if dbColumnName == "" {
			continue
		}
		if _, ok := excludeMap[dbColumnName]; ok {
			continue
		}
		if option.OmitZero && field.IsZero() {
			continue
		}
		if len(option.IncludeColumns) > 0 && includeMap[dbColumnName] == nil {
			continue
		}
		ups[dbColumnName] = field.Value()
	}
	if len(ups) == 0 {
		return nil, fmt.Errorf("no valid fields error")
	}
	return ups, nil
}
